#pragma once

#include <map>

#include "net/instaweb/htmlparse/public/html_filter.h"
using namespace net_instaweb;

#include "Config.h"
#include "Node.h"
#include "Parser.h"

#define INVOKE_CALLBACK(name, ...)

class CFilterAdapter : public HtmlFilter
{
  CParser& m_parser;
  py::object m_handler;
  std::string m_name;

  typedef std::map<std::string, py::object> CallbackMap;
  CallbackMap m_callbacks;

  void FillUpCallbacks(void);
  
  void Invoke(const char *name)
  {
    CallbackMap::const_iterator it = m_callbacks.find(name);

    if (it != m_callbacks.end())
    {
      it->second();
    }
  }

  template <typename T>
  void Invoke(const char *name, T obj)
  {
    CallbackMap::const_iterator it = m_callbacks.find(name);

    if (it != m_callbacks.end())
    {
      py::object arg(py::handle<>(py::to_python_value<T&>()(obj)));

      it->second(arg);
    }
  }
public:
  CFilterAdapter(CParser& parser, py::object handler, const std::string& name);

  static void Expose(void);

  py::object GetHandler(void) const { return m_handler; }
  const std::string GetName(void) const { return m_name; }

  virtual void StartDocument() { Invoke("handle_startdoc"); }
  virtual void EndDocument() { Invoke("handle_endoc"); }

  virtual void StartElement(HtmlElement* element) 
  { Invoke<CHtmlElement>("handle_starttag", CHtmlElement(m_parser.GetParse(), element)); }
  virtual void EndElement(HtmlElement* element) 
  { Invoke<CHtmlElement>("handle_endtag", CHtmlElement(m_parser.GetParse(), element)); }

  virtual void Cdata(HtmlCdataNode* cdata) 
  { Invoke<CHtmlCdataNode>("handle_cdata", CHtmlCdataNode(m_parser.GetParse(), cdata)); }
  virtual void Comment(HtmlCommentNode* comment) 
  { Invoke<CHtmlCommentNode>("handle_comment", CHtmlCommentNode(m_parser.GetParse(), comment)); }

  virtual void Directive(HtmlDirectiveNode* directive) 
  { Invoke<CHtmlDirectiveNode>("handle_decl", CHtmlDirectiveNode(m_parser.GetParse(), directive)); }
  virtual void IEDirective(HtmlIEDirectiveNode* directive) 
  { Invoke<CHtmlIEDirectiveNode>("handle_ie_decl", CHtmlIEDirectiveNode(m_parser.GetParse(), directive)); }
  virtual void Characters(HtmlCharactersNode* characters) 
  { Invoke<CHtmlCharactersNode>("handle_data", CHtmlCharactersNode(m_parser.GetParse(), characters)); }

  virtual void Flush() { Invoke("flush"); }
  virtual const char* Name() const { return m_name.c_str(); }
};
